<script>
import { events } from "./bus";
const pointerSize = 6;
const directions = {
  left: [-1, 0],
  right: [1, 0],
  top: [0, 1],
  bottom: [0, -1]
};

export default {
  name: "Popover",
  render: function(createElement) {
    if (!this.visible) {
      return;
    }

    return createElement(
      "div",
      {
        class: this.className,
        style: this.style,
        attrs: {
          "data-popover": this.name
        },
        on: {
          click(event) {
            event.stopPropagation();
          }
        },
        ref: "dropdown"
      },
      this.$slots.default
    );
  },
  props: {
    name: {
      type: String,
      required: true
    },
    width: {
      type: Number,
      default: 130
    },
    pointer: {
      type: Boolean,
      default: true
    },
    event: {
      type: String,
      default: "click"
    },
    anchor: {
      type: Number,
      default: 0.5,
      validator: v => v >= 0 && v <= 1
    }
  },
  data() {
    return {
      visible: false,
      positionClass: "",
      position: {
        left: 0,
        top: 0
      }
    };
  },
  mounted() {
    events.$on(this.showEventName, this.showEventListener);
    events.$on(this.hideEventName, this.hideEventListener);
  },
  beforeDestroy() {
    events.$off(this.showEventName, this.showEventListener);
    events.$off(this.hideEventName, this.hideEventListener);
  },
  computed: {
    showEventName() {
      return `show:${this.event}`;
    },
    hideEventName() {
      return `hide:${this.event}`;
    },
    className() {
      return ["vue-popover", this.pointer && this.positionClass];
    },
    style() {
      return {
        width: `${this.width}px`,
        ...this.position
      };
    }
  },
  methods: {
    showEventListener(event) {
      if (this.visible) {
        events.$emit(this.hideEventName);
        return;
      }

      this.$nextTick(() => {
        let { target, name, position } = event;

        if (name === this.name) {
          let direction = directions[position];

          this.positionClass = `dropdown-position-${position}`;
          this.visible = true;

          this.$nextTick(() => {
            this.$emit("show", event);

            this.$nextTick(() => {
              let position = this.getDropdownPosition(
                target,
                this.$refs.dropdown,
                direction
              );

              this.position = {
                left: `${position.left}px`,
                top: `${position.top}px`
              };
            });
          });
        }
      });
    },

    hideEventListener(event) {
      if (this.visible) {
        this.visible = false;
        this.$emit("hide", event);
      }
    },

    getDropdownPosition(target, dropdown, direction) {
      let trRect = target.getBoundingClientRect();
      let ddRect = dropdown.getBoundingClientRect();

      // Scroll offset of the current document
      let scrollTop =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop;
      let scrollLeft =
        window.pageXOffset ||
        document.documentElement.scrollLeft ||
        document.body.scrollLeft;

      // Position within the parent
      let offsetLeft = trRect.left + scrollLeft;
      let offsetTop = trRect.top + scrollTop;

      // let shiftX = ddRect.width - trRect.width
      let shiftY = 0.5 * (ddRect.height + trRect.height);

      // Center of the target element
      let centerX = offsetLeft - 0.5 * (ddRect.width - trRect.width);
      let centerY = offsetTop + trRect.height - shiftY;

      // let anchorX = direction[0] * this.anchor
      // let anchorY = direction[0] * this.anchor

      // Position of the dropdown relatively to target
      let x = direction[0] * 0.5 * (ddRect.width + trRect.width);
      let y = direction[1] * shiftY;

      // Pointer size correction
      if (this.pointer) {
        x += direction[0] * pointerSize;
        y += direction[1] * pointerSize;
      }

      return {
        left: centerX + x,
        top: centerY - y
      };
    }
  }
};
</script>

<style>
.vue-popover {
  --pointer-size: 6px;
  display: block;
  position: absolute;
  background: #fff;

  box-shadow: 0px 4px 20px 0px rgba(52, 73, 94, 0.2);

  padding: 5px;
  border-radius: 5px;

  z-index: 998;
}
.vue-popover:before {
  display: block;
  position: absolute;
  width: 0;
  height: 0;
  content: "";
}

.vue-popover.dropdown-position-bottom:before {
  border-left: var(--pointer-size) solid transparent;
  border-right: var(--pointer-size) solid transparent;
  border-bottom: var(--pointer-size) solid #fff;
  top: calc(0 - var(--pointer-size));
  left: calc(50% - var(--pointer-size));
  filter: drop-shadow(0px -2px 2px rgba(52, 73, 94, 0.1));
}

.vue-popover.dropdown-position-top:before {
  border-left: var(--pointer-size) solid transparent;
  border-right: var(--pointer-size) solid transparent;
  border-top: var(--pointer-size) solid #fff;
  bottom: calc(0 - var(--pointer-size));
  left: calc(50% - var(--pointer-size));
  filter: drop-shadow(0px 2px 2px rgba(52, 73, 94, 0.1));
}

.vue-popover.dropdown-position-left:before {
  border-top: var(--pointer-size) solid transparent;
  border-bottom: var(--pointer-size) solid transparent;
  border-left: var(--pointer-size) solid #fff;
  right: calc(0 - var(--pointer-size));
  top: calc(50% - var(--pointer-size));
  filter: drop-shadow(2px 0px 2px rgba(52, 73, 94, 0.1));
}

.vue-popover.dropdown-position-right:before {
  border-top: var(--pointer-size) solid transparent;
  border-bottom: var(--pointer-size) solid transparent;
  border-right: var(--pointer-size) solid #fff;
  left: -var(--pointer-size);
  top: calc(50% - var(--pointer-size));
  filter: drop-shadow(-2px 0px 2px rgba(52, 73, 94, 0.1));
}
</style>
